package w4156.idog.server;

import java.util.ArrayList;
import java.util.List;

import javax.ejb.Stateful;
import javax.naming.InitialContext;
import javax.naming.NamingException;

import w4156.idog.model.Card;
import w4156.idog.model.User;
import w4156.idog.model.User.statusType;

/**
 * Session Bean implementation class TransactionSessionBean
 */
@Stateful(mappedName = "ejb/eBlackjack/TransactionSLSB")
public abstract class TransactionSessionBean implements TransactionSessionBeanRemote {

	private List<UserSessionRemote> tableUsers = null;
	private CardsBeanRemote cards = null;
	private final short Max_Number = 4;
	private int current = 0;// current userid

	private boolean win = false;
	private boolean started = false;
	private boolean saved = false; 

	/**
	 * Default constructor.
	 */
	public TransactionSessionBean() {
		init();
	}

	/**
	 * Initialization for dealer and tableUsers List
	 */
	@Override
	public void init() {
		try {
			//table User List
			tableUsers = new ArrayList<UserSessionRemote>();
			
			//init dealer's data
			User dealer = new User();
			dealer.setObjid(9999999);
			dealer.setName("dealer");
			dealer.setMoney(1000000000000000000.0);
			dealer.setStatus(statusType.Ready);
			insert(dealer);
			
			//game index
			current = 0;
			cards = null;
			
			//init cards Session Bean
			InitialContext context;

			context = new InitialContext();
			cards = (CardsBeanRemote) context
					.lookup("ejb/eBlackjack/CardsSLSB");
			cards.shuffle();
		} catch (NamingException e) {
			e.printStackTrace();
		}
	}

	/**
	 * Bid Operation
	 */
	@Override
	public Card bidOp(int userId, double money) {
		Card newcard = null;
		try {
			//Find user by userid
			if (tableUsers.get(current).getUser().getObjid() == userId) {
				//get card
				newcard = this.cards.dealCard();
				
				//bid
				((PlayerSessionBeanRemote) (tableUsers.get(current)))
						.bidOp(money);
				((PlayerSessionBeanRemote) (tableUsers.get(current)))
						.getcard(newcard);
				//set status to Bid
				tableUsers.get(current).setStatus(statusType.Bid);
			}
			
			//move on to next player
			current = (current + 1) % Max_Number;
			tableUsers.get(current).setStatus(statusType.MyTurn);
			bidpass();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return newcard;
	}

	/**
	 * Hit Operation
	 */
	@Override
	public Card hitOp(int userId) {
		Card newcard = null;
		try {
			//find user by userid
			if (tableUsers.get(current).getUser().getObjid() == userId) {
				// && ((PlayerSessionBeanRemote)(tableUsers.get(0))).point()<=21
				//get a new card
				newcard = this.cards.dealCard();
				((PlayerSessionBeanRemote) (tableUsers.get(current)))
						.getcard(newcard);
				tableUsers.get(current).setStatus(statusType.Hit);
			}
			
			//move on to next player
			current = (current + 1) % Max_Number;
			while ((tableUsers.get(current).getUser().retrieveStatus() == statusType.Stand)
					|| (tableUsers.get(current).getUser().retrieveStatus() == statusType.Surrender)) {
				current = (current + 1) % Max_Number;
			}
			hitpass();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return newcard;
	}

	/**
	 * Double Operation
	 */
	@Override
	public Card doubleOp(int userId) {
		Card newcard = null;
		try {
			//find user by userid
			if (tableUsers.get(current).getUser().getObjid() == userId) {
				// && ((PlayerSessionBeanRemote)(tableUsers.get(0))).point()<=21
				//get a new card
				newcard = this.cards.dealCard();
				PlayerSessionBeanRemote cuplayer = ((PlayerSessionBeanRemote) tableUsers.get(current));
				cuplayer.doubleOp();
				cuplayer.getcard(newcard);
				tableUsers.get(current).setStatus(statusType.Hit);
			}
			
			//move on to next player
			current = (current + 1) % Max_Number;
			while ((tableUsers.get(current).getUser().retrieveStatus() == statusType.Stand)
					|| (tableUsers.get(current).getUser().retrieveStatus() == statusType.Surrender)) {
				current = (current + 1) % Max_Number;
			}
			hitpass();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return newcard;
	}
	
	/**
	 * Bid pass operation
	 */
	private void bidpass() {
		try {
			// dealer bidding
			if (current == 0) {
				if (((PlayerSessionBeanRemote) (tableUsers.get(0))).point() < 17) {
					((PlayerSessionBeanRemote) (tableUsers.get(current)))
							.bidOp(25.00);
					tableUsers.get(current).setStatus(statusType.Bid);
				} else {
					tableUsers.get(current).setStatus(statusType.Stand);
				}
				current = (current + 1) % Max_Number;
				tableUsers.get(current).setStatus(statusType.MyTurn);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * Hit pass operation
	 */
	private void hitpass() {
		try {
			// dealer bidding
			if (current == 0) {
				if (((PlayerSessionBeanRemote) (tableUsers.get(0))).point() < 17) {
					((PlayerSessionBeanRemote) (tableUsers.get(current)))
							.hitOp();
					tableUsers.get(current).setStatus(statusType.Hit);
				} else {
					tableUsers.get(current).setStatus(statusType.Stand);
				}
				current = (current + 1) % Max_Number;
			}

			if (!tryWin()) {
				int t = current;
				for (;; current = (current + 1) % Max_Number) {
					if ((tableUsers.get(current).getUser().retrieveStatus() != statusType.Stand)
							&& (tableUsers.get(current).getUser().retrieveStatus() != statusType.Surrender)){
						tableUsers.get(current).setStatus(statusType.MyTurn);
						break;
					}
					if (((current + 1) % Max_Number) == t)
						break;
				}

				tryWin();
			} else {
				// done = true;
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * Try whether all players are stand, surrender and check win/lose
	 */
	private boolean tryWin() {
		boolean finish = true;
		try {
			//check whether all players are stand
			for (int i = 1; i < 4; i++) {
				if ((tableUsers.get(i).getUser().retrieveStatus() != statusType.Stand)
						&& (tableUsers.get(i).getUser().retrieveStatus() != statusType.Surrender)) {
					finish = false;
				}
			}

			//all stand or surrender
			if (finish) {
				// while (((PlayerSessionBeanRemote)
				// (tableUsers.get(0))).point() < 17) {
				// ((PlayerSessionBeanRemote) (tableUsers.get(0))).hitOp();
				// }
				tableUsers.get(0).setStatus(statusType.Stand);

				//dealer's action
				int dp = ((PlayerSessionBeanRemote) tableUsers.get(0)).point();
				/*
				 * if (dp>21) { dp = 1000; }
				 */

				//count the point for each players and set win/lose status
				for (int i = 1; i < 4; i++) {
					int pp = ((PlayerSessionBeanRemote) tableUsers.get(i))
							.point();
					if (pp <= 21) {
						if ((dp > 21) || (pp > dp)) {
							if (tableUsers.get(i).getUser().retrieveStatus() != statusType.Surrender) {
								((PlayerSessionBeanRemote) tableUsers.get(i))
										.setwin();
							}
						}
					}
				}
				setWin(true);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return finish;
	}

	/**
	 * Stand Operation
	 */
	@Override
	public void standOp(int userId) {
		try {
			//set stand
			tableUsers.get(current).setStatus(statusType.Stand);
			
			//move on to next player
			current = (current + 1) % Max_Number;
			if (!tryWin()) {
				while ((tableUsers.get(current).getUser().retrieveStatus() == statusType.Stand)
						|| (tableUsers.get(current).getUser().retrieveStatus() == statusType.Surrender)) {
					current = (current + 1) % Max_Number;
				}
				hitpass();
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * Surrender Operation
	 */
	@Override
	public void surrenderOp(int userId) {
		try {
			//set stand
			if (!started) {
				for (int i = 0; i<tableUsers.size(); i++)
					if (tableUsers.get(i).getUser().getObjid() == userId) {
						tableUsers.remove(i);
						return;
					}
			}
			tableUsers.get(current).setStatus(statusType.Surrender);
			
			//move on to next player
			current = (current + 1) % Max_Number;
			if (!tryWin()) {
				while ((tableUsers.get(current).getUser().retrieveStatus() == statusType.Stand)
						|| (tableUsers.get(current).getUser().retrieveStatus() == statusType.Surrender)) {
					current = (current + 1) % Max_Number;
				}
				hitpass();
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * Get current players number
	 */
	public int getPlayerNumber() {
		return tableUsers.size();
	}

	/**
	 * Add player into current table
	 */
	@Override
	public void insert(User user) {
		try {
			UserSessionRemote userSession = null;
			//factory mode for the user according to role
			if (user.isAdmin()) {
				InitialContext context = new InitialContext();
				userSession = (UserSessionRemote) ((AdministratorSessionBeanRemote) context
						.lookup("ejb/eBlackjack/AdministratorSLSB"));
				// userSession = (UserSessionBean) new
				// AdministratorSessionBean();
				userSession.setUser(user);
				tableUsers.add((UserSessionRemote) userSession);
			} else {
				InitialContext context = new InitialContext();
				userSession = (UserSessionRemote) ((PlayerSessionBeanRemote) context
						.lookup("ejb/eBlackjack/PlayerSLSB"));
				// userSession = (UserSessionBean) new PlayerSessionBean();
				userSession.setUser(user);
				tableUsers.add(userSession);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * Check whether a player is in current table
	 */
	@Override
	public boolean contains(Integer objid) {
		//iterate to find the user
		for (UserSessionRemote tmpuser : tableUsers) {
			User u = tmpuser.getUser();
			int i = u.getObjid();
			if ((i == objid) && (u.retrieveStatus() != statusType.Surrender)) {
				return true;
			}
		}
		return false;
	}

	/**
	 * set operation for cards
	 */
	@Override
	public void setCards(CardsBean cards) {
		this.cards = cards;
	}

	/**
	 * Set myTurn operation
	 */
	public void setMyTurn() {
		try {
			boolean Allready = false;
			
			//check whether all players are ready
			if (tableUsers.size() == 4) {
				Allready = true;
				for (int i = 0; i < 4; i++) {
					if (tableUsers.get(i).getUser().retrieveStatus() != statusType.Ready) {
						Allready = false;
						break;
					}
				}
			}
			
			//set the first player to myturn
			if (Allready == true) {
				started = true;
				tableUsers.get(1).setStatus(statusType.MyTurn);
				current = 1;
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * Set to Ready operation
	 */
	public void setReady(int userId) {
		try {
			int i = 0;
			User tmp = null;
			
			//set the user with userId to ready
			while (i < tableUsers.size()) {
				tmp = tableUsers.get(i).getUser();
				if (tmp.getObjid() == userId) {
					tableUsers.get(i).setStatus(statusType.Ready);
					break;
				}
				i++;
			}
			
			//if all players are ready, set myturn
			if (tableUsers.size() == 4)
				setMyTurn();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * Get users operation
	 */
	public List<User> getUsers(int userId) {
		List<User> tmplist = new ArrayList<User>();
		try {
			int i = 0;
			int j = 0;
			User tmp = null;
			
			//get all players by the order:
			// Me->Dealer->Other two players
			while (i < tableUsers.size()) {
				tmp = tableUsers.get(i).getUser();
				if (tmp.getObjid() == userId) {
					tmplist.add(tmp);
					if (i > 0)
						tmplist.add(tableUsers.get(0).getUser());
					j = i;
					break;
				}
				i++;
			}

			i = 0;
			while (i < tableUsers.size()) {
				tmp = tableUsers.get(i).getUser();
				if ((i != j) && (i != 0)) {
					tmplist.add(tmp);
				}
				i++;
			}
		} catch (Exception e) {
			e.printStackTrace();
		}

		if (tmplist.size() > 0)
			return tmplist;
		else
			return null;
	}

	/**
	 * Get cards operation for a userId
	 */
	public List<Card> getCards(int userId) {
		List<Card> tmplist = null;
		try {
			//for each user, put the card into list
			for (int i = 0; i < 4; i++) {
				if (tableUsers.get(i).getUser().getObjid() == userId) {
					tmplist = ((PlayerSessionBeanRemote) tableUsers.get(i))
							.getMycards();
					break;
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}

		return tmplist;
	}
	
	@Override
	public List<UserSessionRemote> getTableUsers() {
		return tableUsers;
	}

	@Override
	public void setTableUsers(List<UserSessionRemote> tableUsers) {
		this.tableUsers = tableUsers;
	}
	
	@Override
	public int getCurrent() {
		return current;
	}
	
	@Override
	public boolean isWin() {
		return win;
	}
	
	@Override
	public void setWin(boolean win) {
		this.win = win;
	}
	
	@Override
	public boolean isSaved() {
		return saved;
	}

	@Override
	public void setSaved(boolean saved) {
		this.saved = saved;
	}
}
